import sys, os
from opencv import cv, highgui
from math import sqrt

#si es True se muestra lo q ve la cam
MOSTRAR_V = False#True
#dimensiones, CAM: camara; SCR: pantalla, W: ancho, H: alto
CAM_W = 640
CAM_H = 480
SCR_W = 1280
SCR_H = 800

# orden de los colores B,G,R
COLOR_MIN = (25, 25, 25)
COLOR_MAX = (256, 256, 256)
C_BLANCO = (255,255,255)
C_CLIC = (140,0,140)
C_NO_CLIC = (120,60,120)


class Mouse():
	def __init__(self):
		self.hp_camara = highgui.cvCreateCameraCapture(0)
		highgui.cvNamedWindow("'c' para cerrar", 1)
		self.ax = self.ay = 1
		self.x_ant = self.y_ant = 0
		self.x1 = self.y1 = 10
		self.x2 = self.y2 = 50
		self.ancho1 = self.ancho2 = 0
		self.area1 = self.area2 = 0
		self.pseudo_z = self.Z_anterior = 0
		self.convexo1 = self.convexo2 = False
		axy = (self.ax, self.ay)
		self.amejor = (self.area1, axy, self.ancho1, self.convexo1)
		self.click = False
	
	def correr(self):
		try:
			while 1:
				frame = highgui.cvQueryFrame(self.hp_camara)
				# Crea una imagen de 8-bit y 1-channel con el mismo tamanio q frame
				mascara = cv.cvCreateImage(cv.cvGetSize(frame), 8, 1)

				# encuentra los pixeles en el rango de color y los mete en la mascara
				cv.cvInRangeS(frame, cv.cvScalar(*COLOR_MIN), cv.cvScalar(*COLOR_MAX), mascara)

				memoria = cv.cvCreateMemStorage(0)
				conteo, contornos = cv.cvFindContours(mascara, memoria)
				# itero entre los contornos...
				lista_mejores = []
				if contornos != None:
					for contorno in contornos.hrange():
						area = abs(cv.cvContourArea(contorno))
						# es convexo booleano
						es_convexo = cv.cvCheckContourConvexity(contorno)
						caja = cv.cvBoundingRect(contorno, 0 )
						# centro
						xa, ya = caja.x+caja.width*0.5, caja.y+caja.height*0.5
						ancho = int(caja.width)
						#me quedo con los de area mayor a ...
						if area >= 12:
							lista_mejores.append((area, (xa, ya), ancho, es_convexo))

				#acomodo por area creciente los contornos
				sorted(lista_mejores, key=lambda elemento: elemento[0])
				lista_final = []
				try:
					self.amejor = lista_mejores[len(lista_mejores)-1]
					self.ax, self.ay = self.amejor[1]
				except IndexError:
					pass

				for e in lista_mejores:
					aux_x, aux_y = e[1]
					if (abs(aux_x - self.ax) < 14) and (abs(aux_y - self.ay) < 14):
						pass
					else:
						lista_final.append(e)
				lista_final.append(self.amejor)
				try:
					mejor = lista_final[len(lista_final)-1]
					self.area1, xy1, self.ancho1, self.convexo1 = mejor
					self.x1, self.y1 = xy1
				except IndexError:
					pass
				try:
					mejor2 = lista_final[len(lista_final)-2]
					self.area2, xy2, self.ancho2, self.convexo2 = mejor2								
					self.x2, self.y2 = xy2
				except IndexError:
					#self.x2, self.y2 = (self.x1, self.y1)
					pass

				X, Y, Z = self._calculos()
				
				#dibujo los circulos
				if MOSTRAR_V:
					xy1 = (int(self.x1),int(self.y1))
					xy2 = (int(self.x2),int(self.y2))
					cv.cvCircle(frame, xy1, self.ancho1, C_BLANCO, 2, 8, 0)
					cv.cvCircle(frame, xy2, self.ancho2, C_BLANCO, 2, 8, 0)
					if self.click:
						cv.cvCircle(frame, (X, Y), 3, C_CLIC, 2, 8, 0)
					else:
						cv.cvCircle(frame, (X, Y), 3, C_NO_CLIC, 2, 8, 0)
					highgui.cvShowImage("'c' para cerrar", frame)
				

				X_real = (SCR_W * (CAM_W - X))/CAM_W
				Y_real = (SCR_H * Y)/CAM_H
				#mueve el mouse
				if ((self.x_ant == X_real) and (self.y_ant == Y_real)):
					pass
				else:
					self._movimiento((X_real, Y_real))
				self.x_ant, self.y_ant = X_real, Y_real
				key = highgui.cvWaitKey(15) #espera una tecla 15 ms
				if key == 'c': #si la tecla es 'c', eleva la excepcion
					raise KeyboardInterrupt
		except KeyboardInterrupt:
			self.parar()
			print "\nSe interrumpio el programa a pedido del usuario"


	def _calculos(self):
		dx = abs(self.x1 - self.x2); dy = abs(self.y1 - self.y2)
		minx = min(self.x1, self.x2); miny = min(self.y1, self.y2)
		X = int(minx + (dx/2.0)); Y = int(miny + (dy/2.0))
		dist = sqrt((dx**2)+(dy**2))
		
		self.pseudo_z = max(self.area1, self.area2)
		Z = int((self.pseudo_z / 2.0) - 5) / 10
		estado_z = int(self.Z_anterior - Z) / 3
		self.Z_anterior = Z
		
		if dist < 29 and not self.click:
			self._click_on(1); self.click = True #print "CLICK ON"
		if dist > 31 and self.click:
			self._click_off(1); self.click = False #print "OFF click"
		
		if (dy < 10) and (dx > 27):
			horizontal = True
		else:
			horizontal = False
		
		if horizontal:
			for i in range(abs(estado_z)):
				if estado_z > 0:
					self._clickear(4)#avanzar scroll!
				else:
					self._clickear(5)#retroceder scroll!
		return X, Y, Z

	def _clickear(self, boton):
		cadena = "xdotool click "+str(boton)
		os.system(cadena)

	def _click_on(self, boton):
		cadena = "xdotool mousedown "+str(boton)
		os.system(cadena)

	def _click_off(self, boton):
		cadena = "xdotool mouseup "+str(boton)
		os.system(cadena)

	def _movimiento(self, indice):
		cadena = "xdotool mousemove "+str(indice[0])+" "+str(indice[1])
		os.system(cadena)
	
	def parar(self):
		highgui.cvDestroyWindow("'c' para cerrar")
		for i in range(1,6):
			self._click_off(i)

def main():
	M = Mouse()
	try:
		M.correr()
	finally:
		M.parar()
	return 0

if __name__ == "__main__":
	main()
